#include "mr25hxx.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>

/**
 * @brief  MRAM 控制指令枚举类型定义
 */
typedef enum
{
    WREN = 0x06,  /*Write Enable*/
    WRDI = 0x04,  /*Write Disable*/
    RDSR = 0x05,  /*Read Status Register*/
    WRSR = 0x01,  /*Write Status Register*/
    READ = 0x03,  /*Read Data Bytes*/
    WRITE = 0x02, /*Write Data Bytes*/
    SLEEP = 0xB9, /*Enter Sleep Mode*/
    WAKE = 0xAB,  /*Exit Sleep Mode*/
    DUMMY = 0xFF, /**/
} mr25hxx_command_t;

/**
 * @brief
 *
 * @param p Pointer to a mr25hxx_t structure that contains
 *               the configuration information for the specified MRAM module.
 * @param pio 驱动接口指针
 * @return 0 正常; -1 驱动接口异常
 */
int32_t mr25hxx_init(mr25hxx_t *p, mram_iodrv_t *pio,mr25hxx_chiptype_t ct)
{
    if (!pio->wrcs || !pio->wrhold || !pio->wrwp || !pio->txrxbuf)
        return -1;
    p->io = *pio;
    p->inited = 1;
    statusregister_t sr;
    sr.var = 0;
    mr25hxx_readstatusregister(p, &sr);
    sr.BP = ProtectedAreaNone;
    sr.SRWD = 1;
    sr.WEL = 1;
    mr25hxx_writestatusregister(p, sr);
    sr.var = 0;
    mr25hxx_readstatusregister(p, &sr);
    p->SRWD = sr.SRWD;
    p->BP = sr.BP;
    p->WEL = sr.WEL;
    p->mramtype = ct;
    return 0;
}

/**
 * @brief Sets the Write Enable Latch (WEL) bit in the status register to 1.
 *
 * @param p Pointer to a mr25hxx_t structure that contains
 *               the configuration information for the specified MRAM module.
 * @return 0 正常; -1 驱动接口异常
 */
int32_t mr25hxx_writeenable(mr25hxx_t *p)
{
    uint8_t t = WREN, r;
    if (!p->inited)
    {
        return -1;
    }
    p->io.wrcs(false);
    p->io.txrxbuf(&t, &r, 1);
    p->io.wrcs(true);
    return 0;
}

/**
 * @brief Resets the WEL bit in the status register to 0.
 *
 * @param p Pointer to a mr25hxx_t structure that contains
 *               the configuration information for the specified MRAM module.
 * @return 0 正常; -1 驱动接口异常
 */
int32_t mr25hxx_writedisable(mr25hxx_t *p)
{
    uint8_t t = WRDI, r;
    if (!p->inited)
    {
        return -1;
    }
    p->io.wrcs(false);
    p->io.txrxbuf(&t, &r, 1);
    p->io.wrcs(true);
    return 0;
}
/**
 * @brief Read Status Register (RDSR) command allows the Status Register to be read.
 *
 * @param p Pointer to a mr25hxx_t structure that contains
 *               the configuration information for the specified MRAM module.
 * @param ps 读取状态寄存值存放地址指针
 * @return 0 正常; -1 驱动接口异常
 */
int32_t mr25hxx_readstatusregister(mr25hxx_t *p, statusregister_t *ps)
{
    uint8_t t[2] = {RDSR, DUMMY}, r[2];
    if (!p->inited)
    {
        return -1;
    }
    p->io.wrcs(false);
    p->io.txrxbuf(t, r, 2);
    p->io.wrcs(true);
    ps->var = r[1];
    p->SRWD = ps->SRWD;
    p->BP = ps->BP;
    p->WEL = ps->WEL;
    return 0;
}
/**
 * @brief New values to be written to the Status Register.
 *
 * @param p Pointer to a mr25hxx_t structure that contains
 *               the configuration information for the specified MRAM module.
 * @attention When WEL is reset to 0, writes to all blocks and the status register are protected.
 *          When WEL is set to 1, BP0 and BP1 determine which memory blocks are protected.
 *          While SRWD is reset to 0 and WEL is set to 1, * status register bits BP0 and BP1 can be modified.
 *          Once SRWD is set to 1, WP must be high to modify SRWD, BP0 and BP1.
 * @param s 待写入状态寄存器的值
 * @return 0 正常; -1 驱动接口异常
 */
int32_t mr25hxx_writestatusregister(mr25hxx_t *p, statusregister_t s)
{
    uint8_t t[2] =
        {
            WRSR,
        },
            r[2];
    t[1] = s.var;
    if (!p->inited)
    {
        return -1;
    }
    p->io.wrwp(true);
    mr25hxx_writeenable(p);
    for (int i = 0; i < 4; i++)
    {
    }
    p->io.wrcs(false);
    p->io.txrxbuf(t, r, 2);
    p->io.wrcs(true);
    s.var = 0;
    mr25hxx_readstatusregister(p, &s);
    p->SRWD = s.SRWD;
    p->BP = s.BP;
    p->WEL = s.WEL;
    return 0;
}
/**
 * @brief Write Data Bytes (WRITE) command allows data bytes to be
 *          written starting at an address specified by the 16-bit address.
 *
 * @param p Pointer to a mr25hxx_t structure that contains
 *               the configuration information for the specified MRAM module.
 * @param addr 写入的起始地址
 * @param pbuf 写入数据指针 u8
 * @param len 待写入字计数
 * @return 0 正常; -1 驱动接口异常
 */
int32_t mr25hxx_write(mr25hxx_t *p, uint32_t addr, uint8_t *pbuf, uint32_t len)
{
    uint8_t t[3];
    if (!p->inited)
    {
        return -1;
    }
    p->io.wrcs(false);
    t[0] = WRITE;
    t[1] = addr >> 8;
    t[2] = addr;
    p->io.txrxbuf(t, NULL, 3);
    p->io.txrxbuf(pbuf, NULL, len);
    p->io.wrcs(true);
    return 0;
}
/**
 * @brief Read Data Bytes (READ) command allows data bytes to be
 *          read starting at an address specified by the 16-bit address.
 *
 * @param p Pointer to a mr25hxx_t structure that contains
 *               the configuration information for the specified MRAM module.
 * @param addr 读取的起始地址
 * @param pbuf 读出数据存放地址 u8
 * @param len 读取的字节数
 * @return 0 正常; -1 驱动接口异常
 */
int32_t mr25hxx_read(mr25hxx_t *p, uint32_t addr, uint8_t *pbuf, uint32_t len)
{
    uint8_t t[3];
    if (!p->inited)
    {
        return -1;
    }
    p->io.wrcs(false);
    t[0] = READ;
    t[1] = addr >> 8;
    t[2] = addr;
    p->io.txrxbuf(t, NULL, 3);
    p->io.txrxbuf(NULL, pbuf, len);
    p->io.wrcs(true);
    return 0;
}

/**
 * @brief Enter Sleep Mode (SLEEP).
 *
 * @param p Pointer to a mr25hxx_t structure that contains
 *               the configuration information for the specified MRAM module.
 * @return 0 正常; -1 驱动接口异常
 */
int32_t mr25hxx_sleep(mr25hxx_t *p)
{
    uint8_t t = SLEEP, r;
    if (!p->inited)
    {
        return -1;
    }
    p->io.wrcs(false);
    p->io.txrxbuf(&t, &r, 1);
    p->io.wrcs(true);
    return 0;
}

/**
 * @brief Exit Sleep Mode (WAKE).
 *
 * @param p Pointer to a mr25hxx_t structure that contains
 *               the configuration information for the specified MRAM module.
 * @return 0 正常; -1 驱动接口异常
 */
int32_t mr25hxx_wake(mr25hxx_t *p)
{
    uint8_t t = WAKE, r;
    if (!p->inited)
    {
        return -1;
    }
    p->io.wrcs(false);
    p->io.txrxbuf(&t, &r, 1);
    p->io.wrcs(true);
    return 0;
}
